package propertymgmt.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Version;

import propertymgmt.interfaces.Reportable;

/**
 * Property.java
 * 
 * Represents a Property.  This class is an entity, which means
 * it will be persisted in the database.
 * 
 * @author Brian
 *
 */
@SuppressWarnings("serial")
@Entity(name = "PROPERTY")
@NamedQueries({
	@NamedQuery(name = "Property.getAllProperties",
				query = "SELECT p FROM PROPERTY p"),
	@NamedQuery(name = "Property.getPropertyById",
				query = "SELECT p FROM PROPERTY p WHERE p.propertyId = :propertyId"),
	@NamedQuery(name = "Property.getPropertyByName",
				query = "SELECT p FROM PROPERTY p WHERE p.propertyName = :propertyName")
})

public class Property implements Serializable, Reportable{
	
	@Id
	@Column(name="PROPERTY_ID", nullable = false)
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long propertyId;
	
	@Column(name = "PROPERTY_NAME", nullable = false)
	private String propertyName = null;
	
	@Column(name = "PROPERTY_ADDRESS", nullable = false)
	private String propertyAddress = null;
	
	@Column(name = "SQUARE_FEET", precision = 2)
	private double squareFeet = -1.;
	
	@Column(name = "NUM_ROOMS")
	private int numRooms = -1;
	
	@Column(name = "NUM_BATHROOMS")
	private int numBaths = -1;
	
	@Column(name = "NUM_HALF_BATHROOMS")
	private int numHalfBaths = -1;
	
	@Column(name = "EXTRA_INFO")
	private String extraInfo = null;
	
	@Column(name = "IMAGE")
	private byte imageBytes[];
	
	@Version
	@Column(name = "LAST_UPDATED_TIME")
	private Date updatedTime;
	
	@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
	private List<Lease> leases = null;
	
	/**
	 * Default Constructor
	 */
	public Property(){
		
	}
	
	/**
	 * Constructor
	 * 
	 * @param propertyName String identifier for Property
	 * @param propertyAddress Property address
	 * @param squareFeet square feet of Property
	 * @param numRooms number of rooms for Property
	 * @param numBaths number of bathrooms for Property
	 * @param numHalfBaths number of half bathrooms for Property
	 * @param extraInfo Extra information that should be associated with this Property
	 */
	public Property(String propertyName, String propertyAddress,
			double squareFeet, int numRooms, int numBaths, int numHalfBaths,
			String extraInfo) {

		this.propertyName = propertyName;
		this.propertyAddress = propertyAddress;
		this.squareFeet = squareFeet;
		this.numRooms = numRooms;
		this.numBaths = numBaths;
		this.numHalfBaths = numHalfBaths;
		this.extraInfo = extraInfo;
		this.leases = new ArrayList<Lease>();
	}
	
	/**
	 * Returns this Property's id (primary key)
	 * @return long
	 */
	public long getId(){
		return propertyId;
	}
	
	/**
	 * @param lease
	 */
	public boolean addLease(Lease lease){
		if(!leases.contains(lease)){
			leases.add(lease);
			return true;
		}
		
		return false;
	}
	
	/**
	 * Removes a Lease from this Property
	 * 
	 * @param lease
	 * @return true if successful
	 */
	public boolean removeLease(Lease lease){
		return leases.remove(lease);
	}
	
	/**
	 * Returns Lease with id <code>leaseId</code>
	 * @param leaseId id of Lease
	 * @return Lease (null permitted)
	 */
	public Lease getLease(long leaseId){
		if(leases == null) return null;
		
		for(Lease lease : leases){
			if(lease.getId() == leaseId){
				return lease;
			}
		}
		
		return null;
	}
	
	/**
	 * @return List<Lease>
	 */
	public List<Lease> getLeases(){
		return leases;
	}

	/**
	 * @return the propertyName
	 */
	public String getPropertyName() {
		return propertyName;
	}

	/**
	 * @param propertyName the propertyName to set
	 */
	public void setPropertyName(String propertyName) {
		this.propertyName = propertyName;
	}

	/**
	 * @return the propertyAddress
	 */
	public String getPropertyAddress() {
		return propertyAddress;
	}

	/**
	 * @param propertyAddress the propertyAddress to set
	 */
	public void setPropertyAddress(String propertyAddress) {
		this.propertyAddress = propertyAddress;
	}
	
	/**
	 * @param extraInfo
	 */
	public void setExtraInfo(String extraInfo){
		this.extraInfo = extraInfo;
	}
	
	/**
	 * @param imageBytes
	 */
	public void setImageBytes(byte imageBytes[]){
		if(imageBytes != null){
			this.imageBytes = Arrays.copyOf(imageBytes, imageBytes.length);
		}else {
			this.imageBytes = null;
		}
	}
	
	/**
	 * @return the extraInfo
	 */
	public String getExtraInfo(){
		return extraInfo;
	}
	
	/**
	 * @return the image
	 */
	public byte[] getImageBytes(){
		return imageBytes;
	}
	
	/**
	 * @return the squareFeet
	 */
	public double getSquareFeet() {
		return squareFeet;
	}

	/**
	 * @param squareFeet the squareFeet to set
	 */
	public void setSquareFeet(double squareFeet) {
		this.squareFeet = squareFeet;
	}

	/**
	 * @return the numRooms
	 */
	public int getNumRooms() {
		return numRooms;
	}

	/**
	 * @param numRooms the numRooms to set
	 */
	public void setNumRooms(int numRooms) {
		this.numRooms = numRooms;
	}

	/**
	 * @return the numBaths
	 */
	public int getNumBaths() {
		return numBaths;
	}

	/**
	 * @param numBaths the numBaths to set
	 */
	public void setNumBaths(int numBaths) {
		this.numBaths = numBaths;
	}

	/**
	 * @return the numHalfBaths
	 */
	public int getNumHalfBaths() {
		return numHalfBaths;
	}

	/**
	 * @param numHalfBaths the numHalfBaths to set
	 */
	public void setNumHalfBaths(int numHalfBaths) {
		this.numHalfBaths = numHalfBaths;
	}
	
	/**
	 * Returns true if the supplied dates overlap with an already existing Lease
	 * @param startDate
	 * @param endDate
	 * @return boolean
	 */
	public boolean alreadyHasLeaseDuringDates(Date startDate, Date endDate){
		boolean alreadyHasLease = false;
		
		if(leases != null){
			for(Lease lease : leases){
				Date leaseStartDate = lease.getLeaseStartDate();
				Date leaseEndDate = lease.getLeaseEndDate();
				
				if(startDate.compareTo(leaseStartDate) < 0){
					alreadyHasLease = (endDate.compareTo(leaseStartDate) >= 0);
				}else if(startDate.compareTo(leaseStartDate) > 0){
					alreadyHasLease = (startDate.compareTo(leaseEndDate) <= 0);
				}else {
					alreadyHasLease = true;
				}
				
				if(alreadyHasLease) break;
			}
		}
		
		return alreadyHasLease;
	}
	
	/**
	 * Returns some details about this Property
	 * @return String
	 */
	public String getDetails(){
		String name = "Property " + propertyName + ":";
		String idStr = "\nID:\t\t" + propertyId;
		String addr = "\nAddress:\t" + propertyAddress;
		String sqFeet = "\nSquare Feet:\t" + Double.toString(squareFeet);
		String rooms = "\nRooms:\t\t" + Integer.toString(numRooms);
		String baths = "\nBaths:\t\t" + Integer.toString(numBaths);
		String halfBaths = "\nHalf Baths:\t" + Integer.toString(numHalfBaths);
		
		return name.concat(idStr).concat(addr).concat(sqFeet).concat(rooms).concat(baths).concat(halfBaths);
	}
	
	/* (non-Javadoc)
	 * @see propertymgmt.interfaces.Reportable#generateReport()
	 */
	public String generateReport(){
		StringBuffer strBuffer = new StringBuffer("For Property " + propertyName + ":");
		
		if(leases != null && leases.size() != 0){
			for(Lease lease : leases){
				strBuffer.append("\n\n" + lease.generateReport());
			}
		}
		
		return strBuffer.toString();
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		return propertyName;
	}
}